package com.qixiaobao.nondirect.settle.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.qixiaobao.common.constant.MessageClientConstants;
import com.qixiaobao.common.core.domain.model.LoginUser;
import com.qixiaobao.common.core.rabbitmq.config.RabbitMqConfig;
import com.qixiaobao.common.core.rabbitmq.server.MsgSendServer;
import com.qixiaobao.common.enums.InsureFeeStatusEnum;
import com.qixiaobao.common.exception.UtilException;
import com.qixiaobao.common.utils.SecurityUtils;
import com.qixiaobao.common.utils.StringUtils;
import com.qixiaobao.common.utils.easyExcel.EasyExcelUtil;
import com.qixiaobao.common.utils.uuid.IdUtils;
import com.qixiaobao.insure.domain.MatchInsure;
import com.qixiaobao.insure.mapper.MatchInsureMapper;
import com.qixiaobao.nondirect.settle.domain.InsureFeeReceipt;
import com.qixiaobao.nondirect.settle.domain.InsureFee;
import com.qixiaobao.nondirect.settle.domain.dto.InsureFeeDTO;
import com.qixiaobao.nondirect.settle.domain.vo.InsureFeeChargeVO;
import com.qixiaobao.nondirect.settle.mapper.InsureFeeMapper;
import com.qixiaobao.nondirect.settle.mapper.InsureFeeReceiptMapper;
import com.qixiaobao.nondirect.settle.service.IInsureFeeService;
import com.qixiaobao.system.domain.Dict;
import com.qixiaobao.system.domain.Message;
import com.qixiaobao.system.domain.SignCompany;
import com.qixiaobao.system.domain.dto.ChargeDTO;
import com.qixiaobao.system.domain.dto.CountDTO;
import com.qixiaobao.system.mapper.DictMapper;
import com.qixiaobao.system.mapper.SignCompanyMapper;
import com.qixiaobao.system.mapper.SupplyMapper;
import com.qixiaobao.system.mapper.SysUserMapper;
import com.qixiaobao.system.service.IMessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @Description 保费结算 业务层实现
 * @Author yangxm
 * @Date 2025/6/27 16:14
 */
@Slf4j
@Service
public class InsureFeeServiceImpl implements IInsureFeeService {

    @Autowired
    private InsureFeeMapper insureFeeMapper;
    @Autowired
    private DictMapper dictMapper;
    @Autowired
    private SignCompanyMapper signCompanyMapper;
    @Autowired
    private MatchInsureMapper matchInsureMapper;
    @Autowired
    private SupplyMapper supplyMapper;
    @Autowired
    private MsgSendServer mgSendServer;
    @Autowired
    private InsureFeeReceiptMapper insureFeeReceiptMapper;
    @Autowired
    private IMessageService messageService;
    @Autowired
    private SysUserMapper sysUserMapper;


    /**
     * @Description 待支付、已结算列表
     * @Author yangxm
     * @Date 2025/6/27 16:51
     * @param insureFeeDTO
     * @return java.util.List<com.qixiaobao.nondirect.domain.InsureFee>
     */
    @Override
    public List<InsureFee> selectInsureFeeList(InsureFeeDTO insureFeeDTO) {
        long begin = System.currentTimeMillis();
        if (StringUtils.isBlank(insureFeeDTO.getInsureFeeStatus())){
            throw new UtilException("参数：保费结算状态不能为空！");
        }
        String insureFeeStatus = insureFeeDTO.getInsureFeeStatus();
        //用枚举判断状态不属于枚举里面的数据
        if (!InsureFeeStatusEnum.isValidStatus(insureFeeStatus)) {
            throw new UtilException("参数：保费结算状态错误！");
        }
        String insureFeeStatusVal = InsureFeeStatusEnum.fromCode(insureFeeStatus).getInfo();
        log.info("查询非直投结算管理——{}列表，参数：{}", insureFeeStatusVal, insureFeeDTO);
        List<InsureFee> list = insureFeeMapper.selectInsureFeeList(insureFeeDTO);
        if (StringUtils.isNotEmpty(list)){

            //付款类型
            Map<String, String> insureFeePayTypeMap = dictMapper.selectDictListByType("insure_fee_pay_type").stream().collect(Collectors.toMap(Dict::getK, Dict::getVal));
            //付款公司
            Map<Long, String> payCompanyMap = signCompanyMapper.selectSignCompanyNameList().stream().collect(Collectors.toMap(SignCompany::getId, SignCompany::getCompanyName));

            InsureFee insureFee;
            for (int i = 0; i < list.size(); i++) {
                insureFee = list.get(i);
                insureFee.setPayCompanyName(payCompanyMap.get(insureFee.getPayCompanyId()));
                insureFee.setActualPayCompanyName(payCompanyMap.get(insureFee.getActualPayCompanyId()));
                insureFee.setInsureFeePayTypeVal(insureFeePayTypeMap.get(insureFee.getInsureFeePayType()));
                insureFee.setFinanceReckonVal("0".equals(insureFee.getFinanceReckon()) ? "否" : "是");
                insureFee.setSettlePerson("增员:" + insureFee.getAddNum() + ";减员:" + insureFee.getMinusNum());
            }
        }
        log.info("查询非直投结算管理——{}列表，总耗时：{}ms", insureFeeStatusVal, System.currentTimeMillis() - begin);
        return list;
    }

    /**
     * @Description 待支付、已结算 - 结算人数和费用总计统计
     * @Author yangxm
     * @Date 2025/6/30 13:36
     * @param insureFeeDTO
     * @return com.qixiaobao.nondirect.invoice.domain.domain.dto.CountDto
     */
    @Override
    public CountDTO selectInsureFeeTotal(InsureFeeDTO insureFeeDTO) {
        return insureFeeMapper.selectInsureFeeTotal(insureFeeDTO);
    }

    /**
     * @Description 待支付、已结算 - 导出
     * @Author yangxm
     * @Date 2025/6/30 13:37
     * @param insureFeeDTO
     * @param response
     * @return void
     */
    @Override
    public void insureFeeExport(InsureFeeDTO insureFeeDTO, HttpServletResponse response) {
        List<InsureFee> list = selectInsureFeeList(insureFeeDTO);
        String insureFeeStatusVal = InsureFeeStatusEnum.fromCode(insureFeeDTO.getInsureFeeStatus()).getInfo();
        String fileName = "非直投结算管理-" + insureFeeStatusVal;
        List<String> arrayHeaders = new ArrayList<>();
        arrayHeaders.add("序号");
        arrayHeaders.add("供应商全称");
        arrayHeaders.add("供应商简称");
        arrayHeaders.add("付款单位");
        arrayHeaders.add("实际付款单位");
        arrayHeaders.add("结算人数");
        arrayHeaders.add("预回票时间");
        arrayHeaders.add("费用总计");
        arrayHeaders.add("实付保费");
        arrayHeaders.add("财务计入");
        arrayHeaders.add("操作时间");
        arrayHeaders.add("保费归属期");
        if (InsureFeeStatusEnum.SETTLED.getCode().equals(insureFeeDTO.getInsureFeeStatus())){
            arrayHeaders.add("回单金额");
            arrayHeaders.add("回单编号");
            arrayHeaders.add("付款日期");
        }
        arrayHeaders.add("付款类型");
        arrayHeaders.add("备注");
        //动态表头
        List<List<String>> headList = new ArrayList<>();
        List<String> head = null;
        for (String strHead : arrayHeaders) {
            head = new ArrayList<>();
            head.add(strHead);
            headList.add(head);
        }
        //动态表头的数据
        List<List<Object>> datalist = new ArrayList<>();
        List<Object> obj = null;
        InsureFee item;
        for (int i = 0; i < list.size(); i++) {
            item = list.get(i);
            obj = new ArrayList<>();
            obj.add(i + 1);
            obj.add(item.getSupplyName());
            obj.add(item.getSupplyShortName());
            obj.add(item.getPayCompanyName());
            obj.add(item.getActualPayCompanyName());
            obj.add(item.getSettlePerson());
            obj.add(item.getInvoiceReturnDate());
            obj.add(item.getInsureFeeTotal());
            obj.add(item.getActualInsureFee());
            obj.add(item.getFinanceReckonVal());
            if (InsureFeeStatusEnum.WAIT_PAY.getCode().equals(insureFeeDTO.getInsureFeeStatus())){
                obj.add(Optional.ofNullable(item.getUpdateTime()).map(x -> DateFormatUtils.format(x, "yyyy/MM/dd HH:mm:ss")).orElse(""));
            }else {
                obj.add(Optional.ofNullable(item.getPaymentTime()).map(x -> DateFormatUtils.format(x, "yyyy/MM/dd HH:mm:ss")).orElse(""));
            }
            obj.add(item.getInsureFeeMonth());
            if (InsureFeeStatusEnum.SETTLED.getCode().equals(insureFeeDTO.getInsureFeeStatus())){
                obj.add(item.getTransAmounts());
                obj.add(item.getBillNums());
                obj.add(item.getTransDateTimes());
            }
            obj.add(item.getInsureFeePayTypeVal());
            obj.add(item.getRemark() + " " + item.getRemarkOperator() + " " + item.getRemarkTime());
            datalist.add(obj);
        }
        try {
            EasyExcelUtil.writeExcel(response, datalist, headList, fileName, fileName);
        } catch (IOException e) {
            log.error("{}导出异常：", fileName, e);
            throw new RuntimeException("导出失败：" + e.getMessage());
        }
    }

    /**
     * @Description 待支付、已结算 - 费用详情列表
     * @Author yangxm
     * @Date 2025/6/30 14:42
     * @param chargeDTO
     * @return java.util.List<com.qixiaobao.nondirect.invoice.domain.domain.Charge>
     */
    @Override
    public List<InsureFeeChargeVO> selectInsureFeeChargeList(ChargeDTO chargeDTO) {
        long begin = System.currentTimeMillis();
        if (chargeDTO.getInsureFeeId() == null){
            throw new UtilException("参数：保费ID不能为空！");
        }
        if (StringUtils.isBlank(chargeDTO.getInsureFeeStatus())){
            throw new UtilException("参数：保费结算状态不能为空！");
        }
        String insureFeeStatus = chargeDTO.getInsureFeeStatus();
        //用枚举判断状态是否有效
        if (!InsureFeeStatusEnum.isValidStatus(insureFeeStatus)) {
            throw new UtilException("参数：保费结算状态错误！");
        }
        String insureFeeStatusVal = InsureFeeStatusEnum.fromCode(insureFeeStatus).getInfo();
        log.info("查询非直投结算管理——{}费用详情列表，参数：{}", insureFeeStatusVal, chargeDTO);
        List<InsureFeeChargeVO> list = insureFeeMapper.selectChargeListByInsureFeeId(chargeDTO);
        log.info("查询非直投结算管理——{}费用详情列表sql，耗时：{}ms", insureFeeStatusVal, System.currentTimeMillis() - begin);
        if (StringUtils.isNotEmpty(list)){
            //获取所有的保单id
            List<Long> insureIdList = list.stream().map(InsureFeeChargeVO::getInsureId).distinct().collect(Collectors.toList());
            //根据保单id，查询匹配后供应商简称
            List<MatchInsure> matchInsureList = matchInsureMapper.selectToSupplyShortNameList(insureIdList);
            Map<Long,String> toSupplyShortNameMap = null;
            if (StringUtils.isNotEmpty(matchInsureList)){
                toSupplyShortNameMap = matchInsureList.stream().collect(Collectors.toMap(MatchInsure::getFromInsureId, MatchInsure::getToSupplyShortName));
            }
            //字典
            List<Dict> dictList = dictMapper.selectDictList();
            Map<String, Map<String, String>> dictMap = new ConcurrentHashMap<>();
            if (StringUtils.isNotEmpty(dictList)) {
                dictMap = dictList.stream()
                        .filter(c -> c.getVal() != null)
                        .collect(Collectors.groupingBy(Dict::getType, Collectors.toMap(Dict::getK, Dict::getVal)));
            }
            Map<String, String> mealNameMap = dictMap.getOrDefault("mealname", Collections.emptyMap());
            Map<String, String> maimRatioMap = dictMap.getOrDefault("maimRatio", Collections.emptyMap());
            Map<String, String> insurePeriodMap = dictMap.getOrDefault("insureperiod", Collections.emptyMap());
            Map<String, String> jobTypeMap = dictMap.getOrDefault("jobtype", Collections.emptyMap());
            //付款公司
            Map<Long, String> payCompanyMap = signCompanyMapper.selectSignCompanyNameList().stream().collect(Collectors.toMap(SignCompany::getId, SignCompany::getCompanyName));

            InsureFeeChargeVO item;
            for (int i = 0; i < list.size(); i++) {
                item = list.get(i);
                item.setIndex(i + 1);
                if (toSupplyShortNameMap != null) {
                    item.setToSupplyShortName(toSupplyShortNameMap.getOrDefault(item.getInsureId(), ""));
                }
                item.setPayCompanyName(payCompanyMap.getOrDefault(item.getPayCompanyId(), ""));
                item.setActualPayCompanyName(payCompanyMap.getOrDefault(item.getActualPayCompanyId(), ""));
                item.setMealNameVal(mealNameMap.getOrDefault(item.getMealName(), ""));
                item.setMaimRatioVal(maimRatioMap.getOrDefault(item.getMaimRatio(), ""));
                item.setInsurePeriodVal(insurePeriodMap.getOrDefault(item.getInsurePeriod(), ""));
                item.setJobTypeVal(jobTypeMap.getOrDefault(item.getJobType(), ""));
                item.setInsurePerson("增员:" + item.getAddNum() + ";减员:" + item.getMinusNum());
                item.setAbandonFlagVal(item.getAbandonFlag() == 1 ? "是" : "否");
                item.setFinanceReckonVal("0".equals(item.getFinanceReckon()) ? "否" : "是");
                if (insureFeeStatus.equals(InsureFeeStatusEnum.WAIT_PAY.getCode())){
                    item.setInsureFeeSettleTime(item.getCreateTime());
                }else {
                    item.setInsureFeeSettleTime(item.getPaymentTime());
                }
            }
        }
        return list;
    }

    /**
     * @Description 修改保费归属期
     * @Author yangxm
     * @Date 2025/7/1 9:44
     * @param insureFeeDTO
     * @return int
     */
    @Override
    public int updateInsureFeeMonth(InsureFeeDTO insureFeeDTO) {
        return insureFeeMapper.updateInsureFeeMonth(insureFeeDTO);
    }

    /**
     * @Description 修改备注
     * @Author yangxm
     * @Date 2025/7/1 10:25
     * @param insureFeeDTO 
     * @return int
     */
    @Override
    public int updateRemark(InsureFeeDTO insureFeeDTO) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser == null){
            throw new UtilException("登录已失效，请重新登录！");
        }
        insureFeeDTO.setRemarkOperator(loginUser.getUsername());
        return insureFeeMapper.updateRemark(insureFeeDTO);
    }

    /**
     * @Description 下载回单凭证
     * @Author yangxm
     * @Date 2025/7/1 13:11
     * @param insureFeeDTO
     */
    @Override
    public void downloadReceipt(InsureFeeDTO insureFeeDTO) {
        LoginUser user = SecurityUtils.getLoginUser();
        if (user == null){
            throw new IllegalArgumentException("登陆超时，请重新登陆");
        }
        if (StringUtils.isEmpty(insureFeeDTO.getSupplyIdList())){
            throw new UtilException("参数：供应商简称不能为空！");
        }
        if (StringUtils.isBlank(insureFeeDTO.getInsureFeeStatus())){
            throw new UtilException("参数：保费结算状态不能为空！");
        }
        String insureFeeStatus = insureFeeDTO.getInsureFeeStatus();
        //用枚举判断状态是否有效
        if (!InsureFeeStatusEnum.isValidStatus(insureFeeStatus)) {
            throw new UtilException("参数：保费结算状态错误！");
        }
        String insureFeeStatusVal = InsureFeeStatusEnum.fromCode(insureFeeStatus).getInfo();
        String operationType = "非直投结算管理-" + insureFeeStatusVal;
        log.info("一键下载银行回单，操作人：{}, 操作页面：{}, 参数：{}", user.getUsername(), operationType, insureFeeDTO);
        String supplyShortNames;
        List<String> supplyShortNameList = supplyMapper.selectShortNameByIdList(insureFeeDTO.getSupplyIdList());
        if (StringUtils.isEmpty(supplyShortNameList)){
            log.error("一键下载银行回单异常，未查到供应商简称，操作人：{}, 操作页面：{}", user.getUsername(), operationType);
            throw new UtilException("未查到供应商简称");
        }
        supplyShortNames = StringUtils.join(supplyShortNameList, ",");
        //查询需要下载的银行回单ID集合
        List<Long> idList = insureFeeMapper.selectInsureFeeReceiptIdList(insureFeeDTO);
        log.info("一键下载银行回单，操作人：{}, 操作页面：{}, 银行回单ID集合：{}", user.getUsername(), operationType, idList);
        if (StringUtils.isNotEmpty(idList)){
            //将信息放入mq
            JSONObject message = new JSONObject();
            message.put("userId", user.getId());
            message.put("client", "supply");
            message.put("ids", idList);
            message.put("supplyShortName", supplyShortNames);
            message.put("operationType", operationType);
            mgSendServer.sendDirectByRoute(RabbitMqConfig.DIRECT_EXCHANGE, RabbitMqConfig.PAYMENT_ROUTING_KEY, message.toJSONString(), new CorrelationData(IdUtils.simpleUUID()));
        }
    }

    /**
     * @Description 根据保费id，查询回单凭证列表
     * @Author yangxm
     * @Date 2025/7/1 13:52
     * @param insureFeeId
     * @return java.util.List<com.qixiaobao.nondirect.domain.InsureFeeReceipt>
     */
    @Override
    public List<InsureFeeReceipt> selectInsureFeeReceiptListByInsureFeeId(Long insureFeeId) {
        List<InsureFeeReceipt> list = insureFeeReceiptMapper.selectInsureFeeReceiptListByInsureFeeId(insureFeeId);
        //若银行回单明细不存在，则查询保费中的银行回单明细
        if (StringUtils.isEmpty(list)){
            list = insureFeeMapper.selectInsureFeeReceiptListById(insureFeeId);
        }
        return list;
    }

    /**
     * @Description 修改预回票时间
     * @Author yangxm
     * @Date 2025/7/1 14:13
     * @param insureFeeDTO
     * @return int
     */
    @Override
    public int updateInvoiceReturnDate(InsureFeeDTO insureFeeDTO) {
        //查询旧的预回票时间
        InsureFee oldInsureFee = insureFeeMapper.selectOldInvoiceReturnDate(insureFeeDTO.getInsureFeeId());
        if (oldInsureFee == null){
            throw new UtilException("未查到相关数据，请刷新页面后重试");
        }
        //若旧预回票时间存在且不相同，更新时则给财务所有人员发送站内信
        if (StringUtils.isNotBlank(oldInsureFee.getInvoiceReturnDate()) && !insureFeeDTO.getInvoiceReturnDate().equals(oldInsureFee.getInvoiceReturnDate())){
            String content = "预回票时间变更，请查看。";
            Message message = new Message();
            message.setClient(MessageClientConstants.CLIENT_BI);
            message.setSubject(content);
            message.setContent(content);
            message.setLink("pages/supplyInvoice/supplyInvoiceList.html");
            messageService.insertBiMessage(message, sysUserMapper.selectBiUserIdList());
        }
        //更新预回票时间
        return insureFeeMapper.updateInvoiceReturnDate(insureFeeDTO);
    }

    /**
     * @Description 根据保费id，查询回单凭证(主子表结构)
     * @Author yangxm
     * @Date 2025/7/2 8:49
     * @param insureFeeId
     * @return com.qixiaobao.nondirect.domain.InsureFee
     */
    @Override
    public InsureFee selectInsureFeeReceiptById(Long insureFeeId) {
        return insureFeeMapper.selectInsureFeeReceiptByInsureFeeId(insureFeeId);
    }


}
